﻿/*
 * 名称：INI读写类
 * 
 * 作用：实现对INI文件读写的封装，只需要简单的两三个参数即可使用，包括三个主要函数：
 *       public string ReadValue(string lpApplicationName, string lpKeyName)
 *       public bool WriteValue(string lpApplicationName, string lpKeyName, string lpString)
 *       public string ReadValue(string lpApplicationName, string lpKeyName, string lpDefault)
 * 
 * 作者：郝  伟
 * 
 * 时间：2010-5-5   初步建立这几个方法。   
 *      2011-6-3   添加Section属性，这样就可以直接写值。 
 *      2020/06/30 修改变量名称，使之更加简单易懂，同时添加了两个静态方法Write和Read，在简单应用场景下不需要初始化对象。
 *      2021/09/21 添加了对String，Int，Double类型的读取。
 * 
 */

using System.Text;
using System.Runtime.InteropServices;
using System.IO;
using System;

namespace QDasConverter.Utils
{
    /// <summary>
    /// 此类用于实现对INI文件的读写，包括了读，写两个方法，同时还有文件位置属性及默认输出属性。
    /// </summary>
    public class IniAccess
    {
        #region API及属性定义 
        // 注：以下两函数的路径需要使用绝对路径。
        [DllImport("kernel32")]
        private static extern bool GetPrivateProfileString(string lpApplicationName, string lpKeyName, string lpDefault, StringBuilder lpReturnedString, int nSize, string lpFileName);
        [DllImport("kernel32")]
        private static extern bool WritePrivateProfileString(string lpApplicationName, string lpKeyName, string lpString, string lpFileName);

        /// <summary>
        /// 读取或设置所操作的INI文件的位置。
        /// </summary>
        public string FilePath = "config.ini";

        /// <summary>
        /// 读取或设置读取失败的信息。
        /// </summary>
        public string DefaultError = "Failed to read.";


        /// <summary>
        /// 默认的段，初始化为 SYSTEM。分类包括：SYSTEM=系统相关参数, USER=用户相关信息, UI=界面相关配置, MISC=其他]
        /// </summary>
        public string Category = "SYSTEM";

        public static string DefaultConfigFile { get; internal set; }

        #endregion

        #region 构造函数
        /// <summary>
        /// 此类用于实现对INI文件的读写，包括了读，写两个方法，同时还有文件位置属性及默认输出属性。
        /// </summary>
        public IniAccess()
        {
            FilePath = DefaultConfigFile ?? Path.GetFullPath(FilePath);
        }

        /// <summary>
        /// 此类用于实现对INI文件的读写，包括了读，写两个方法，同时还有文件位置属性及默认输出属性。
        /// </summary>
        /// <param name="path">要操作的INI文件位置，如果不存在会自动创建。</param>
        public IniAccess(string path)
        {
            FilePath = Path.GetFullPath(path);
        }

        internal int ReadInt(object lastResID, int v)
        {
            throw new NotImplementedException();
        }
        #endregion

        #region 值读取

        /// <summary>
        /// 读取INI文件中的值，读取成功返回值字符串。如果key不存在，则返回null。
        /// </summary>
        /// <param name="category">字段的类别。</param>
        /// <param name="key">字段名。</param>
        /// <param name="defaultValue">读取失败的返回值。</param>
        /// <returns>字段值.</returns>
        public string ReadValue(string key, string defaultValue = "")
        {
            return ReadValue(Category, key, defaultValue);
        }

        /// <summary>
        /// 读取INI文件中的值，读取成功返回值字符串。如果key不存在，则返回null。
        /// </summary>
        /// <param name="category">字段的类别。</param>
        /// <param name="key">字段名。</param>
        /// <param name="defaultValue">读取失败的返回值。</param>
        /// <returns>字段值.</returns>
        public string ReadValue(string category, string key, string defaultValue = "")
        {
            StringBuilder sb = new StringBuilder(1024);
            bool res = GetPrivateProfileString(category, key, defaultValue, sb, sb.Capacity, FilePath);
            return res ? sb.ToString() : defaultValue;
        }

        /// <summary>
        /// 根据默认分类，使用主键读取值，如果值不存则返回 null。
        /// </summary>
        /// <param name="key"></param>
        /// <returns></returns>
        public string ReadString(string key, bool decrypt = false)
        {
            var res = ReadValue(Category, key, null);
            if (decrypt && res != null)
                res = Decrypt(res);
            return res;
        }


        /// <summary>
        /// 使用指定密钥对数据使用AES进行加密，如果密钥为空，则使用默认密码解密。如果加密失败，则返回null。
        /// </summary>
        /// <param name="toBeEncrypted">待加密文本。</param>
        /// <param name="key">加密的密钥，默认密码为"DataSecuryty"的MD5值的前16个字符。</param>
        /// <returns></returns>
        public static string Encrypt(string toBeEncrypted, string key = null)
        {
            if (toBeEncrypted == null)
                return null;

            if (toBeEncrypted.Length == 0)
                return "";

            string encrypted = null;
            try
            {
                key = key ?? WindGoes6.Security.MD5Helper.StringHash("DataSecuryty").Substring(0, 16);
                encrypted = WindGoes6.Security.AES.Encrypt(toBeEncrypted, key);
            }
            catch (Exception ex)
            {
                Console.WriteLine(ex.Message);
            }
            return encrypted;
        }

        /// <summary>
        /// 读取日期，如果日期格式解析错误，返回defaultDatetime。默认分类为 SYSTEM。
        /// </summary>
        /// <param name="key">读取时的密钥。</param>
        /// <param name="defaultDatetime">解释失败时返回的时间。</param>
        /// <returns></returns>
        public DateTime ReadDateTime(string key, DateTime defaultDatetime)
        {
            return ReadDateTime(Category, key, defaultDatetime);
        }

        /// <summary>
        /// 读取日期，如果日期格式解析错误，返回defaultDatetime。
        /// </summary>
        /// <param name="category">分类。</param>
        /// <param name="key">读取时的密钥。</param>
        /// <param name="defaultDatetime">解释失败时返回的时间。</param>
        /// <returns></returns>
        public DateTime ReadDateTime(string category, string key, DateTime defaultDatetime)
        {
            try
            {
                return DateTime.Parse(ReadString(category, key));
            }
            catch (Exception ex)
            {
                Console.WriteLine("Function.ReadDateTime(): " + ex.Message);
            }
            return defaultDatetime;
        }

        /// <summary>
        /// 使用指定密钥对数据使用AES进行解密，如果密钥为空，则使用默认密码解密。如果解密失败，则返回null。
        /// </summary>
        /// <param name="toBeDecrypted">待解密文本。</param>
        /// <param name="key">解密密钥，默认密码为"DataSecuryty"的MD5值的前16个字符。</param>
        /// <returns></returns>
        public static string Decrypt(string toBeDecrypted, string key = null)
        {
            if (toBeDecrypted == null)
                return null;

            if (toBeDecrypted.Length == 0)
                return "";

            string decrypted = null;
            try
            {
                key = key ?? WindGoes6.Security.MD5Helper.StringHash("DataSecuryty").Substring(0, 16);
                decrypted = WindGoes6.Security.AES.Decrypt(toBeDecrypted, key);
            }
            catch (Exception ex)
            {
                Console.WriteLine(ex.Message);
            }
            return decrypted;
        }

        static string Decrpt1(string text)
        {
            if (string.IsNullOrEmpty(text))
                return null;

            StringBuilder sb = new StringBuilder();
            for (int i = 0; i < text.Length; i++)
                sb.Append((char)(text[i] - 3));

            return sb.ToString();
        }


        static string Encrypt1(string text)
        {
            if (string.IsNullOrEmpty(text))
                return null;

            StringBuilder sb = new StringBuilder();
            for (int i = 0; i < text.Length; i++)
                sb.Append((char)(text[i] + 3));

            return sb.ToString();
        }

        /// <summary>
        /// 根据默认分类，使用主键读取字符串的值，返回defaultValue。
        /// </summary>
        /// <param name="key">字段名</param>
        /// <param name="defaultValue">读取失败时返回的缺省值。</param>
        /// <returns></returns>
        public string ReadString(string key, string defaultValue, bool decrypt = false)
        {
            var res = ReadValue(Category, key, null);
            if (res == null)
                return defaultValue;
            else if (decrypt)
                res = Decrypt(res);
            return res;
        }

        /// <summary>
        /// 根据分类和主键读取字符串的值，返回defaultValue。
        /// </summary>
        /// <param name="category">分类名。</param>
        /// <param name="key">字段名</param>
        /// <param name="defaultValue">读取失败时返回的缺省值。</param>
        /// <returns></returns>
        public string ReadString(string category, string key, string defaultValue, bool decrypt = false)
        {
            var res = ReadValue(category, key, null);
            if (res == null)
                return defaultValue;
            else if (decrypt)
                res = Decrypt(res);
            return res;
        }


        public int ReadInt(string key, int defaultValue = 0)
        {
            StringBuilder sb = new StringBuilder(1024);
            return GetPrivateProfileString(Category, key, defaultValue + "", sb, sb.Capacity, FilePath)
                && int.TryParse(sb.ToString(), out int v0) ? v0 : defaultValue;
        }

        public int ReadInt(string category, string key, int defaultValue)
        {
            StringBuilder sb = new StringBuilder(1024);
            return GetPrivateProfileString(category, key, defaultValue + "", sb, sb.Capacity, FilePath)
                && int.TryParse(sb.ToString(), out int v0) ? v0 : defaultValue;
        }


        public double ReadDouble(string key, double defaultValue = 0)
        {
            StringBuilder sb = new StringBuilder(1024);
            return GetPrivateProfileString(Category, key, defaultValue + "", sb, sb.Capacity, FilePath)
                && double.TryParse(sb.ToString(), out double v0) ? v0 : defaultValue;
        }

        public double ReadDouble(string category, string key, double defaultValue = 0)
        {
            StringBuilder sb = new StringBuilder(1024);
            return GetPrivateProfileString(category, key, defaultValue + "", sb, sb.Capacity, FilePath)
                && double.TryParse(sb.ToString(), out double v0) ? v0 : defaultValue;
        }

        #endregion

        #region 写入值

        /// <summary>
        /// 读取属性的值。
        /// </summary>
        /// <param name="section">字段的类别。</param>
        /// <param name="key">字段名。</param>
        /// <param name="value">要写入的值。</param>
        /// <returns>返回值表示是否执行成功。</returns>
        public bool WriteValue(string section, string key, string value, bool encrypt = false)
        {
            return WritePrivateProfileString(section, key, encrypt ? Encrypt(value) : value, FilePath);
        }


        /// <summary>
        /// 读取属性的值。
        /// </summary>
        /// <param name="key">字段名。</param>
        /// <param name="value">字段值。</param>
        /// <returns>返回值表示是否执行成功。</returns>
        public bool WriteValue(string key, string value, bool encrypt = false)
        {
            return WriteValue(Category, key, value, encrypt);
        }



        /// <summary>
        /// 完整的数据写入静态方法，包括分类、键、值、加密和文件五类信息。
        /// </summary>
        /// <param name="category">分类。</param>
        /// <param name="key">键。</param>
        /// <param name="value">值。</param>
        /// <param name="filepath">文件路径。</param>
        /// <param name="encrypt">是否加密。</param>
        /// <returns></returns>
        public static bool Write(string category, string key, string value, string filepath, bool encrypt = false)
        {
            filepath = Path.GetFullPath(filepath);
            value = encrypt && string.IsNullOrEmpty(value) ? value : Decrypt(value);
            return WritePrivateProfileString(category, key, value, filepath);
        }

        #endregion
    }
}

